package com.ayz.gateway.assist.config;

import cn.hutool.core.util.StrUtil;

import com.ayz.gateway.assist.application.GatewayContextCloseListener;
import com.ayz.gateway.assist.application.GatewayContextStartListener;
import com.ayz.gateway.common.constants.GatewayServiceData;
import com.ayz.gateway.core.context.context.ApiGatewayContext;
import com.ayz.gateway.core.context.context.ApiGatewayContextFactoryBuilder;
import com.ayz.gateway.core.core.Netty.NettyServerConfig;
import com.ayz.gateway.core.datasource.DatasourceFactory;
import com.ayz.gateway.core.datasource.DatasourceType;
import com.ayz.gateway.core.datasource.unpooled.UnpooledDatasourceFactory;
import com.ayz.gateway.core.rpc.config.DubboConfiguration;
import com.ayz.gateway.core.session.processor.RpcPostProcessorRegistry;
import com.ayz.gateway.sdk.gatawayService.GatewayServiceSdk;
import com.ayz.gateway.sdk.gatawayService.cache.ApiDataRepository;
import com.ayz.gateway.sdk.gatawayService.config.GatewayServiceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * @ClassName GatewayAutoConfiguration
 * @Description TODO: 网关服务的自动装配类
 * @Author 快乐的星球
 * @Date 2023/5/25 10:42
 * @Version 1.0
 **/
@Configuration(
        proxyBeanMethods = false
)
/* 当前这个注解是SpringBoot在配置文件中的 spring.cloud.api.gateway.enabled=ture 的时候才会自动装配*/
@ConditionalOnProperty(
        name = {"api.gateway.enabled"},
        matchIfMissing = true
)
/* 完成当前类装配的时候先完成 配置类属性的自动加载  */
@EnableConfigurationProperties({GatewayServiceProperties.class})
public class GatewayAutoConfiguration {
    @Autowired
    private GatewayServiceProperties serviceProperties;
    /**
     * 当前微服务应用程序的名称
     */
    @Value("${spring.application.name}")
    private String applicationName;

    /**
     * 注入网关上下文的全局配置参数
     * 存储网关上下文的全局配置参数和动态代理对象的存储
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public com.ayz.gateway.core.context.config.Configuration  configuration(){
        String rpcType = serviceProperties.getRpcType();
        String registryAddress = serviceProperties.getRpcRegistryAddress();
        switch (rpcType){
            case "Dubbo":
                return new DubboConfiguration(applicationName,registryAddress);
            default:
                throw new IllegalArgumentException("目前不支持配置的Rpc资源:{"+rpcType+"}!");
        }
    }

    /**
     * 网关通信组件依赖于Netty
     * 完成网关通信组件的开发
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public NettyServerConfig nettyServerConfig(){
        NettyServerConfig nettyServerConfig=new NettyServerConfig();
        nettyServerConfig.setListenPort(serviceProperties.getNetty().getPort());
        nettyServerConfig.setEventLoopGroupBossThreads(serviceProperties.getNetty().getBossThreads());
        nettyServerConfig.setServerWorkerThreads(serviceProperties.getNetty().getWorkerThreads());
        nettyServerConfig.setNioSoBackLog(serviceProperties.getNetty().getBacklog());
        nettyServerConfig.setUseEpollWhenUseLinux(serviceProperties.getNetty().isUseEpollWhenLinux());
        nettyServerConfig.setServerChannelMaxIdleTimeSeconds(serviceProperties.getNetty().getChannelMaxIdleTimeSeconds());
        return nettyServerConfig;
    }

    /**
     * 将具体的RPC资源抽象成连接资源
     * 由Datasource管理
     * 当前DatasourceFactory管理Datasource的创建
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public DatasourceFactory datasourceFactory(com.ayz.gateway.core.context.config.Configuration  configuration){
        DatasourceType datasourceType=null;
        //TODO: 确定RPC连接资源的类型
        String rpcType = serviceProperties.getRpcType();
        switch (rpcType){
            case "Dubbo":
                datasourceType=DatasourceType.DUBBO;
                break;
            default:
                throw new IllegalArgumentException("目前不支持配置的Rpc资源:{"+rpcType+"}!");
        }
        return new UnpooledDatasourceFactory(configuration,datasourceType);
    }

    /**
     * 发起RPC泛化调用的时候，通过执行器去执行RPC执行计划的时候
     * 可以使用前置和后置增强器去进行增强
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public RpcPostProcessorRegistry rpcPostProcessorRegistry(){
        return new RpcPostProcessorRegistry();
    }

    /**
     * 网关上下文构建工厂
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public ApiGatewayContextFactoryBuilder apiGatewayContextFactoryBuilder(){
        return new ApiGatewayContextFactoryBuilder();
    }

    /**
     * ApiGatewayContext API网关上下文
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public ApiGatewayContext apiGatewayContext(ApiGatewayContextFactoryBuilder builder,
                                               com.ayz.gateway.core.context.config.Configuration  configuration,
                                               NettyServerConfig nettyServerConfig,
                                               DatasourceFactory datasourceFactory,
                                               RpcPostProcessorRegistry rpcPostProcessorRegistry){
        return builder.build(configuration,nettyServerConfig,datasourceFactory,rpcPostProcessorRegistry);
    }

    /**
     * 完成网关上下文的启动工作
     * @param apiGatewayContext
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public GatewayContextStartListener gatewayContextStartListener(ApiGatewayContext apiGatewayContext, GatewayServiceSdk gatewayServiceSdk){
        return new GatewayContextStartListener(apiGatewayContext,serviceProperties,gatewayServiceSdk);
    }

    /**
     * 网关上下文关闭的监听器
     * @param apiGatewayContext
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public GatewayContextCloseListener gatewayContextCloseListener(ApiGatewayContext apiGatewayContext, GatewayServiceSdk gatewayServiceSdk){
        return new GatewayContextCloseListener(apiGatewayContext,serviceProperties,gatewayServiceSdk);
    }

    /**
     * 网关通信组件的启动
     */

    /**
     * 网关服务通信组件SDK的配置
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public GatewayServiceConfig gatewayServiceConfig(){
        String serverAddresses = this.serviceProperties.getCenter().getAddress();
        if(StrUtil.isBlank(serverAddresses)){
            throw new IllegalArgumentException("当前网关服务并没有配置网关注册中心的地址,启动失败!");
        }
        String[] serverAddressArray = serverAddresses.split(",");
        GatewayServiceConfig gatewayServiceConfig=new GatewayServiceConfig();
        for (String address : serverAddressArray) {
            gatewayServiceConfig.addServerAddress(address);
        }
        return gatewayServiceConfig;
    }

    /**
     * 网关服务的基础信息
     * 用于向网关注册中心注册使用
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public GatewayServiceData gatewayServiceData(){
        GatewayServiceProperties serviceProperties = this.serviceProperties;
        //组装GatewayServiceData对象
        GatewayServiceData gatewayServiceData=new GatewayServiceData();
        gatewayServiceData.setGatewayId(serviceProperties.getGatewayId());
        gatewayServiceData.setGatewayName(serviceProperties.getGatewayName());
        gatewayServiceData.setGatewayGroup(serviceProperties.getGatewayGroup());
        gatewayServiceData.setAddress(GatewayServiceData.generateServerAddress(serviceProperties.getHost(),serviceProperties.getNetty().getPort()));
        return gatewayServiceData;
    }

    /**
     * 当前网关服务应该管理的API接口的存储容器
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public ApiDataRepository apiDataRepository(){
        return new ApiDataRepository();
    }

    /**
     * 网关服务的网络通信组件SDK
     * @param gatewayServiceConfig 网关服务的网络组件SDK配置
     * @param gatewayServiceData  网关服务的基础信息
     *
     * @param apiDataRepository   网关服务管理的API存储容器
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public GatewayServiceSdk gatewayServiceSdk(GatewayServiceConfig gatewayServiceConfig, GatewayServiceData gatewayServiceData,
                                               ApiDataRepository apiDataRepository){
        return new GatewayServiceSdk(gatewayServiceConfig,gatewayServiceData,apiDataRepository);
    }
}
